#include <QDir>
#include "leafsegmentation.h"
#include "leafcurvature.h"
#include "leafdatabase.h"

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>

#include <iostream>
#include <fstream>

#include <pthread.h>

using namespace cv;
using namespace std;

#define NUM_THREADS     1

std::vector<LeafDatabase::Entry> features;
void *PrintHello(void *threadid)
{
   unsigned long tid;
   tid = (unsigned long)threadid;
   unsigned int stride=features.size()/NUM_THREADS+1;
   unsigned int start=tid*stride;
   unsigned int end=std::min((tid+1)*stride,features.size());
   std::cout<<start<<" "<<end<<std::flush;
   for(unsigned int i=start;i<end;i++)
   {
       std::cout<<i<<" "<<std::flush;
       Mat leaf_img = imread(features[i].full_path.c_str(),CV_LOAD_IMAGE_COLOR);
       pyrDown( leaf_img, leaf_img, Size( leaf_img.cols/2, leaf_img.rows/2 ));

       ImageMask leaf_mask=LeafSegmentation::segment(leaf_img, 2, 2);
       ImageMask cleaned_mask=LeafSegmentation::removeFalsePositive(leaf_mask,0.3,2);

       // second attempt
       Scalar area=sum(cleaned_mask.mMask);
       int num_cluster=2;
       while(*(area.val)<50&&num_cluster<10) {
           num_cluster++;
           leaf_mask=LeafSegmentation::segment(leaf_img, num_cluster, 2);
           cleaned_mask=LeafSegmentation::removeFalsePositive(leaf_mask,0.1,2);

           area=sum(cleaned_mask.mMask);
       }

       ImageMask destemed_mask=LeafSegmentation::removeStem(cleaned_mask);
       //ImageMask boundary_mask=LeafCurvature::boundary(destemed_mask.mMask);

       Mat white_img(leaf_img.size(), CV_8UC3);white_img.setTo(Vec3b(255,255,255));
       Mat output = destemed_mask.applyTo(&white_img);
       imwrite(features[i].segmented_full_path.c_str(), output );

       area=sum(destemed_mask.mMask);
       //if((float)area/(float)(destemed_mask.mMask.size().area)<)
       if(*(area.val)<50) continue;

       destemed_mask=LeafSegmentation::resizeMask(destemed_mask);
       destemed_mask.boundary();

       for(int radius=2;radius<=50;radius+=2){
           std::vector<float> arclength_histogram=LeafCurvature::normalizedArclengthHistogram(destemed_mask,radius,21);
           features[i].arclength_feature.insert(features[i].arclength_feature.end(),arclength_histogram.begin(),arclength_histogram.end());
           std::vector<float> area_histogram=LeafCurvature::normalizedAreaHistogram(destemed_mask,radius,21);
           features[i].area_feature.insert(features[i].area_feature.end(),area_histogram.begin(),area_histogram.end());
       }
   }
   pthread_exit(NULL);
}

int main(int, char**)
{
    QDir current_dir=QDir::current();
    current_dir.cdUp();current_dir.cdUp();
    current_dir.cd("leafsnap-dataset/dataset");

    QString image_dir_name("images");

    QString segmented_dir_name("our_segmented");
    if(!current_dir.exists(segmented_dir_name))
        current_dir.mkdir(segmented_dir_name);

    QDir image_dir=current_dir;image_dir.cd(image_dir_name);
    QDir segmented_dir=current_dir;segmented_dir.cd(segmented_dir_name);

    std::cout << "Enter "<<image_dir_name.toStdString()<<" directory..." << std::endl;
    QFileInfoList category_list = image_dir.entryInfoList();
    for (int i = 0; i < category_list.size(); ++i) {
        QFileInfo category_fileInfo = category_list.at(i);
        if(category_fileInfo.isHidden())continue;

        if(category_fileInfo.isDir()){
            image_dir.cd(category_fileInfo.fileName());
            if(!segmented_dir.exists(category_fileInfo.fileName()))
                segmented_dir.mkdir(category_fileInfo.fileName());
            segmented_dir.cd(category_fileInfo.fileName());

            std::cout << "Enter "<<category_fileInfo.fileName().toStdString()<<" directory..." << std::endl;
            QFileInfoList species_list = image_dir.entryInfoList();
            for (int i = 0; i < species_list.size(); ++i) {
                QFileInfo species_fileInfo = species_list.at(i);
                if(species_fileInfo.isHidden())continue;

                image_dir.cd(species_fileInfo.fileName());
                if(!segmented_dir.exists(species_fileInfo.fileName()))
                    segmented_dir.mkdir(species_fileInfo.fileName());
                segmented_dir.cd(species_fileInfo.fileName());

                std::cout << "Enter "<<species_fileInfo.fileName().toStdString()<<" directory..." << std::endl;
                QFileInfoList image_list = image_dir.entryInfoList();
                for (int i = 0; i < image_list.size(); i+=1) {
                    std::cout<<i<<" "<<std::flush;
                    QFileInfo image_fileInfo = image_list.at(i);
                    if(image_fileInfo.isHidden())continue;

                    //if(rand()%1000>1) continue;

                    LeafDatabase::Entry new_entry;
                    new_entry.type=category_fileInfo.fileName().toStdString();
                    new_entry.name=species_fileInfo.fileName().toStdString();
                    new_entry.filename=image_fileInfo.fileName().toStdString();
                    new_entry.full_path=image_fileInfo.absoluteFilePath().toStdString();
                    new_entry.segmented_full_path=(/*segmented_dir.absolutePath()+*/"collection/"+image_fileInfo.fileName()).toStdString();
                    features.push_back(new_entry);
                }
                std::cout<<std::endl;
                image_dir.cdUp();
                segmented_dir.cdUp();
            }
            image_dir.cdUp();
            segmented_dir.cdUp();
        }
    }

    pthread_t threads[NUM_THREADS];
    pthread_attr_t attr;
    int rc;
    long t;
    void *status;

    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);

    time_t start=clock();
    for(t=0; t<NUM_THREADS; t++){
       printf("In main: creating thread %ld\n", t);
       rc = pthread_create(&threads[t], &attr, PrintHello, (void *)t);
       if (rc){
          printf("ERROR; return code from pthread_create() is %d\n", rc);
          exit(-1);
       }
    }

    pthread_attr_destroy(&attr);
    for(t=0; t<NUM_THREADS; t++) {
        rc = pthread_join(threads[t], &status);
        if (rc) {
            printf("ERROR; return code from pthread_join() is %d\n", rc);
            exit(-1);
        }
        printf("Main: completed join with thread %ld having a status of %ld\n",t,(long)status);
    }

    std::cout<<(float)(clock()-start)/(float)CLOCKS_PER_SEC<<std::endl;

//    //#pragma omp parallel for
//    for(unsigned int i=0;i<features.size();i++){
//        std::cout<<i<<" "<<std::flush;
//        Mat leaf_img = imread(features[i].full_path.c_str(),CV_LOAD_IMAGE_COLOR);
//        pyrDown( leaf_img, leaf_img, Size( leaf_img.cols/2, leaf_img.rows/2 ));

//        ImageMask leaf_mask=LeafSegmentation::segment(leaf_img, 2);
//        ImageMask cleaned_mask=LeafSegmentation::removeFalsePositive(leaf_mask,0.3,2);
//        ImageMask destemed_mask=LeafSegmentation::removeStem(cleaned_mask);
//        //ImageMask boundary_mask=LeafCurvature::boundary(destemed_mask.mMask);

//        Mat output = destemed_mask.applyTo(&leaf_img);
//        imwrite(features[i].segmented_full_path.c_str(), output );

//        Scalar area=sum(destemed_mask.mMask);
//        //if((float)area/(float)(destemed_mask.mMask.size().area)<)
//        if(*(area.val)<50) continue;

//        for(int radius=2;radius<=50;radius+=2){
//            std::vector<float> arclength_histogram=LeafCurvature::normalizedArclengthHistogram(destemed_mask.mMask,radius,21);
//            features[i].arclength_feature.insert(features[i].arclength_feature.end(),arclength_histogram.begin(),arclength_histogram.end());
//            std::vector<float> area_histogram=LeafCurvature::normalizedAreaHistogram(destemed_mask.mMask,radius,21);
//            features[i].area_feature.insert(features[i].area_feature.end(),area_histogram.begin(),area_histogram.end());
//        }
//    }

    ofstream area_feature_file((current_dir.absolutePath()+"/area_feature.csv").toStdString().c_str());
    ofstream arclength_feature_file((current_dir.absolutePath()+"/arclength_feature.csv").toStdString().c_str());

    for(unsigned int i=0;i<features.size();i++){
        std::cout<<i<<" "<<std::flush;

        if(features[i].arclength_feature.size()==0)continue;

        arclength_feature_file<<features[i].type<<","<<features[i].name<<","<<features[i].filename;
        area_feature_file<<features[i].type<<","<<features[i].name<<","<<features[i].filename;

        for(unsigned int j=0;j<features[i].arclength_feature.size();j++){
            arclength_feature_file<<","<<features[i].arclength_feature[j];
            area_feature_file<<","<<features[i].area_feature[j];
        }

        arclength_feature_file<<std::endl;
        area_feature_file<<std::endl;
    }

    arclength_feature_file.close();
    area_feature_file.close();

    std::cout<<"Total number of image="<<features.size()<<std::endl;

    /* Last thing that main() should do */
    //pthread_exit(NULL);
    return 0;
}
